from sqlalchemy.sql.sqltypes import SmallInteger, Integer
from sqlalchemy.sql.schema import Column
from flask_sqlalchemy import SQLAlchemy as _SQLAlchemy, BaseQuery
from contextlib import contextmanager
from datetime import datetime


class SQLAlchemy(_SQLAlchemy):
    @contextmanager
    def auto_commit(self):
        try:
            yield  #上文执行后会返回继续往下执行
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e

#每一查询语句filter_by中都有status=1这个参数来判断软删除的状态
#可以通过重写filter_by方法(面向对象方法)来给所有的查询语句filter_by来设置这个条件判断
#而不需要在每一句filter_by方法中人工添加status=1这个条件

#自定义的MyQuery类必须继承flask_sqlalchemy的BaseQuery才能对filter_by方法进行改写
class MyQuery(BaseQuery):
    def filter_by(self, **kwargs):
        kwargs['status'] = 1
        #通过super可以使用父类的方法
        return super(MyQuery, self).filter_by(**kwargs)
    
    #暂时还不清楚怎么重写filter方法，后面有时间再看
    #def filter(self, *criterion):


#生成db实例时，要通过query_class=MyQuery(默认是query_class=BaseQuery)指定自己定义的类
#重写的filter_by方法才能生效并应用到实际项目上
db = SQLAlchemy(query_class=MyQuery)

#共有属性在基类中设置，其他类用继承则可
class Base(db.Model):
    __abstract__ = True  #抽象模型，不会创建实体表，否则需要创建主键
    #default=1表示未删除，=0表示已删除
    status = Column(SmallInteger, default=1)
    
    #增加一个创建时间的属性,用于记录实例对象创建的时间
    create_time = Column('create_time', Integer)
    #注意!!!  这里不能通过在类的create_time中
    #设置default=int(datetime.now().timestamp()
    #来记录创建的时间, 这样会导致所有数据库中的记录时间会是一样的
    #因为类变量的定义是发生在类的定义的过程中，而不是实例的初始化过程中
    #这是类的类变量和实例变量的很重要的区别  !!!!

    #正确的方法是在__init__设置对象创建的时间    
    def __init__(self):
        #获取系统时间戳作为对象创建的时间
        self.create_time = int(datetime.now().timestamp())
    
    #设置列属性的方法(所有实例共有)
    def set_attrs(self, attrs_dict):
        for key, value in attrs_dict.items():
            #判断对象是否有key这个属性，暂时还不理解key not in ['id']的意思
            if hasattr(self, key) and key not in ['id']:
                setattr(self, key, value)
           
    #增加个属性(方法),将实例对象创建的时间戳格式转换为datetime格式    
    @property
    def create_datetime(self):
        if self.create_time:
            return datetime.fromtimestamp(self.create_time)  
        else:
            return None      